﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Net;
using System.IO;
using Newtonsoft.Json;
using System.Transactions;

namespace SOAServiceCoreClient
{
    class Program
    {
        static List<Task> _taskList;
        static List<int> _threadIdList;
        static int _totalFailed;
        static int _count;
        static bool _timeout;
        static AutoResetEvent _ae = new AutoResetEvent(false);
        static string url = "http://192.168.81.197/Vacation/ProductList/Query";//"http://localhost:64555/json/productlistsearch";//"http://localhost:8800/restapi";//  "http://169.254.139.11:8800/restapi";//
        //public static ManualResetEvent allDone = new ManualResetEvent(false);
        //const int BUFFER_SIZE = 1024;
        //const int DefaultTimeout = 2 * 60 * 1000; // 2 minutes timeout 

        //// Abort the request if the timer fires. 
        //private static void TimeoutCallback(object state, bool timedOut)
        //{
        //    if (timedOut)
        //    {
        //        H5VacationProductServiceClient request = state as H5VacationProductServiceClient;
        //        if (request != null)
        //        {
        //            request.
        //            request.Abort();
        //        }
        //    }
        //}

        static void Main(string[] args)
        {
            Console.WriteLine(ServicePointManager.DefaultConnectionLimit);
            Console.WriteLine(string.Format("Process Id:{0}", Process.GetCurrentProcess().Id));
            Console.WriteLine("press any key to start");
            Console.ReadKey(true);
            Console.WriteLine("started");
            //WebApi();
            //MysqlConcurrentTest();
            CServiceStackTest();
            Console.WriteLine("press anykey to exit");
            Console.ReadKey();
        }

        private static void CServiceStackTest()
        {
            _totalFailed = 0;
            _threadIdList = new List<int>();
            _timeout = false;
            ProductListSearchRequestType request = new ProductListSearchRequestType();
            request.dCtyId = 2;
            request.destKwd = "上海";
            request.flag = 0;
            request.pageIdx = 1;
            request.qparams = new List<QueryParamItem> { new QueryParamItem { type = 1, val = "3" } };
            request.sortRule = 3;
            request.sortType = 2;
            request.ver = 0;

            //H5VacationProductServiceClient syncClient = H5VacationProductServiceClient.GetInstance("http://169.254.139.11:3001/restapi");
            //syncClient.SetOperationTimeout("ProductListSearch", CServiceStack.ServiceClient.RequestTimeoutType.Timeout, new TimeSpan(0, 0, 0, 0, 5000));
            //syncClient.Format = "json";
            //syncClient.ProductListSearch(request);
            //System.Net.ServicePointManager.DefaultConnectionLimit = 1024;
            //System.Net.HttpWebRequest req = new System.Net.HttpWebRequest();
            //req.ServicePoint.ConnectionLimit
            //    syncClient.
            //_taskList = new List<Task>();
            Stopwatch sw = new Stopwatch();
            H5VacationProductServiceClient client = new H5VacationProductServiceClient(url);//H5VacationProductServiceClient.GetInstance(url);
            //client.LocalHttpWebRequestFilter = new Action<HttpWebRequest>(req =>
            //{
            //    //if (req.ServicePoint.ConnectionLimit > 5)
            //    //{
            //    //    req.ServicePoint.ConnectionLimit = 5;
            //    //}
            //    //req.KeepAlive = false;
            //    //if (req.ServicePoint.ConnectionLeaseTimeout != 0)
            //    //{
            //    //    req.ServicePoint.ConnectionLeaseTimeout = 0;
            //    //}
            //});
            //client.Timeout = TimeSpan.FromMilliseconds(2);
            //client.SetOperationTimeout("ProductListSearch", CServiceStack.ServiceClient.RequestTimeoutType.Timeout, new TimeSpan(0, 0, 12));
            client.Format = "json";
            for (int j = 0; j < 1; j++)
            {
                _taskList = new List<Task>();
                sw.Restart();
                //int count = 0;

                int batchSize = 100;
                List<Task> taskList = new List<Task>();
                for (int i = 0; i < batchSize; i++)
                {
                    //_taskList.Add(IOCPCall(client, request));
                    var t = IOCPCall(client, request);
                    taskList.Add(t);
                }
                Console.WriteLine("iocp waiting...");
                Task.WaitAll(_taskList.ToArray());
                //while (_count < batchSize - 1)
                //{
                //    Thread.SpinWait(200);
                //}

                //Task.WaitAll(taskList.ToArray(), 2);

                _ae.WaitOne();

                Console.WriteLine("errors:" + _errorCounter);
                StringBuilder sb = new StringBuilder();
                sb.Append("thread used by iocp:" + _threadIdList.Count + Environment.NewLine);
                sb.Append(string.Format("iocp total:{0}ms" + Environment.NewLine, sw.Elapsed.TotalMilliseconds));
                sb.Append("total failed:" + _totalFailed + Environment.NewLine);
                sb.Append(string.Format("completed {0}" + Environment.NewLine, _count));
                Console.WriteLine(sb.ToString());

                //for (int i = 0; i < batchSize; i++)
                //{
                //    //    H5VacationProductServiceClient client = H5VacationProductServiceClient.GetInstance("http://169.254.139.11:8800/restapi");
                //    //    client.Format = "json";
                //    //    client.SetOperationTimeout("ProductListSearch", CServiceStack.ServiceClient.RequestTimeoutType.Timeout, new TimeSpan(0, 0, 10));
                //    _taskList.Add(ASyncCall(client, request));
                //}
                //Console.WriteLine("async waiting...");
                //Task.WaitAll(_taskList.ToArray());
                //Console.WriteLine("thread used by async:" + _threadIdList.Count);
                //Console.WriteLine("total failed:" + _totalFailed);
                //Console.WriteLine(string.Format("async total:{0}s", sw.Elapsed.TotalSeconds));
                //Console.WriteLine(string.Format("completed tasks total:{0}", _count));
            }
            _timeout = true;
            //taskList.Clear();
            //sw.Restart(); 


            sw.Stop();
        }
        static int _errorCounter = 0;
        private static Task IOCPCall(H5VacationProductServiceClient client, ProductListSearchRequestType request)
        {
            //H5VacationProductServiceClient newClient = H5VacationProductServiceClient.GetInstance("http://192.168.81.197/restapi");// ("http://169.254.139.11:8800/restapi");
            client = new H5VacationProductServiceClient(url);
            client.LocalHttpWebRequestFilter = new Action<HttpWebRequest>(req =>
            {
                //if (req.ServicePoint.ConnectionLimit > 5)
                //{
                //    req.ServicePoint.ConnectionLimit = 5;
                //}
                //req.KeepAlive = false;
                if (req.ServicePoint.ConnectionLeaseTimeout != 0)
                {
                    req.ServicePoint.ConnectionLeaseTimeout = 0;
                }
            });
            int tid = Thread.CurrentThread.ManagedThreadId;
            var task = client.StartIOCPTaskOfProductListSearch(request)
                //CreateAsyncTaskOfProductListSearch(request);//

            //task
                .ContinueWith(t =>
            {
                try
                {
                    //var test = t.Result;
                    int threadId = Thread.CurrentThread.ManagedThreadId;
                    string flag = "I am in callback ";

                    Console.WriteLine(flag + threadId);

                    //if (t.IsFaulted || t.Exception != null)
                    //{
                    //    //Interlocked.Add(ref _totalFailed, 1);
                    //    if (_timeout)
                    //    {
                    //        Console.WriteLine(string.Format("total fauts:{0}", _totalFailed));
                    //    }
                    //    if (t.Exception != null)
                    //    {
                    //        Console.WriteLine("ex start");
                    //        if (t.Exception.InnerExceptions.Count > 1)
                    //        {
                    //            Console.WriteLine("more than 1 ex");
                    //        }
                    //        t.Exception.Handle((innerEx) =>
                    //        {
                    //            var cstEx = innerEx as CServiceStack.ServiceClient.CServiceException;
                    //            if (cstEx != null)
                    //            {
                    //                Console.WriteLine(string.Format("error code:{0}, message: {1}", cstEx.ErrorCode, cstEx.Message));
                    //            }
                    //            else
                    //            {
                    //                Console.WriteLine(innerEx);
                    //            }
                    //            return true;
                    //        });
                    //    }
                    //    //if (t.Exception.InnerExceptions != null)
                    //    //{
                    //    //    Console.WriteLine("ex start");
                    //    //    foreach (var innerEx in t.Exception.InnerExceptions)
                    //    //    {
                    //    //        var cstEx = innerEx as CServiceStack.ServiceClient.CServiceException;
                    //    //        if (cstEx != null)
                    //    //        {
                    //    //            Console.WriteLine(string.Format("error code:{0}, message: {1}", cstEx.ErrorCode, cstEx.Message));
                    //    //        }
                    //    //        else
                    //    //        {
                    //    //            Console.WriteLine(innerEx);
                    //    //        }
                    //    //    }
                    //    //    Console.WriteLine("ex end");
                    //    //}
                    //}
                }
                catch (Exception ex)
                {
                    Interlocked.Add(ref _errorCounter,1);
                    Console.WriteLine(ex);
                }
                finally
                {
                    Interlocked.Add(ref _count, 1);
                    if (_count == 1000 - 1)
                    {
                        _ae.Set();
                    }
                }
            }
            , TaskContinuationOptions.OnlyOnFaulted
            );
            //task.Start();
            return task;
        }

        private static Task ASyncCall(H5VacationProductServiceClient client, ProductListSearchRequestType request)
        {
            var task = Task.Factory.StartNew<ProductListSearchResponseType>(() =>
            {
                try
                {
                    return client.ProductListSearch(request);
                }
                catch (Exception ex)
                {
                    Interlocked.Add(ref _totalFailed, 1);
                }
                return null;
            }).ContinueWith(t =>
            {

                if (t.IsFaulted || t.Exception != null)
                {
                    //Console.WriteLine(t.Exception);
                    Interlocked.Add(ref _totalFailed, 1);
                }
                else
                {
                    if (t.IsCompleted)
                    {
                        Interlocked.Add(ref _count, 1);
                        //Console.WriteLine(t.Result.products.Count);
                    }
                }
                int threadId = Thread.CurrentThread.ManagedThreadId;
                if (!_threadIdList.Contains(threadId))
                {
                    _threadIdList.Add(threadId);
                }
                //Console.WriteLine("thread used by async:" + Process.GetCurrentProcess().Threads.Count);
            });
            //task.Start();
            return task;
        }
        private static void WebApi()
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            List<WaitHandle> handles = new List<WaitHandle>();
            int counter = 1;
            //AutoResetEvent ae = new AutoResetEvent(false);
            object syncRoot = new object();
            Monitor.Enter(syncRoot);
            //Monitor.Wait(syncRoot);
            //Monitor.Pulse(syncRoot);
            for (int i = 0; i < counter; i++)
            {
                HttpWebRequest request = HttpWebRequest.Create("http://localhost:60500/api/ProductListSearchResponseType") as HttpWebRequest;//"http://192.168.81.197/restapi/json/productlistsearch"
                request.Method = "GET";
                request.ContentType = "application/json";
                Console.WriteLine("connections:" + request.ServicePoint.CurrentConnections);
                Console.WriteLine("header:" + request.Connection);
                //var result = 
                request.BeginGetResponse(new AsyncCallback((r1) =>
                {
                    try
                    {
                        //Task.Factory.StartNew(() =>
                        //{

                        HttpWebRequest req1 = r1.AsyncState as HttpWebRequest;
                        var response = req1.EndGetResponse(r1);
                        Stream streamResponse = response.GetResponseStream();
                        StreamReader streamRead = new StreamReader(streamResponse);
                        string responseString = streamRead.ReadToEnd();
                        var responseDTO = JsonConvert.DeserializeObject<ProductListSearchResponseType>(responseString);

                        streamResponse.Close();
                        streamRead.Close();

                        //Release the HttpWebResponse
                        response.Close();

                        Console.WriteLine("web api done!");

                        Interlocked.Add(ref _count, 1);
                        if (_count == counter - 1)
                        {
                            lock (syncRoot)
                            {
                                //Monitor.Wait(syncRoot);
                                Monitor.Pulse(syncRoot);
                            }
                            //ae.Set();
                        }
                        //});
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex);
                    }
                }), request);

                //handles.Add(result.AsyncWaitHandle);
                //if ((i + 1) % 64 == 0)
                //{
                //    Console.WriteLine("waiting");
                //    WaitHandle.WaitAll(handles.ToArray());
                //    Console.WriteLine("done");
                //    handles.Clear();
                //}
            }
            //}), request);
            //result.AsyncWaitHandle

            //if (handles.Count > 0)
            //{
            //    int j = 0;
            //    //handles.Take(64).ToArray();
            //    while (true)
            //    {
            //        var handlesArray = handles.Skip(j).Take(64).ToArray();
            //        Console.WriteLine("waiting");
            //        WaitHandle.WaitAll(handlesArray);
            //        if (handlesArray.Count() < 64)
            //        {
            //            break;
            //        }
            //        j += 64;
            //    }
            //}
            //while (_count < counter-1)
            //{
            //    Thread.SpinWait(200); 
            //}
            //ae.WaitOne();
            Monitor.Wait(syncRoot);
            Monitor.Exit(syncRoot);
            Console.WriteLine(string.Format("all done in {0} ms", sw.ElapsedMilliseconds));

        }

        private static void MysqlConcurrentTest()
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            List<Task> taskList = new List<Task>();
            for (int i = 0; i < 10000; i++)
            {
                taskList.Add(Task.Factory.StartNew((index) =>
                {
                    using (TransactionScope trans = new TransactionScope())
                    {
                        using (MySql.Data.MySqlClient.MySqlConnection connection = new MySql.Data.MySqlClient.MySqlConnection("Server=localhost;Uid=root;Database=world;pwd=P@ssw0rd"))
                        {
                            string cmdText = string.Format("INSERT IGNORE INTO city (Name, CountryCode, District, Population) VALUES('DemoCity{0}', 'PRC', 'SH', 1880000)", index);
                            MySql.Data.MySqlClient.MySqlCommand cmdCity = new MySql.Data.MySqlClient.MySqlCommand(cmdText, connection);
                            connection.Open();
                            int result = cmdCity.ExecuteNonQuery();

                            Console.WriteLine(string.Format("result{0}:{1}", index, result));

                            cmdText = string.Format(@"INSERT IGNORE INTO countrylanguage (CountryCode, Language, IsOfficial, Percentage) VALUES
	('PRC', '{0}', 'T', 5.3)", index);
                            MySql.Data.MySqlClient.MySqlCommand cmdCountry = new MySql.Data.MySqlClient.MySqlCommand(cmdText, connection);
                            result = cmdCountry.ExecuteNonQuery();

                            Console.WriteLine(string.Format("result{0}:{1}", index, result));
                            trans.Complete();
                        }
                    }
                }, i).ContinueWith(t =>
                 {
                     if (t.IsFaulted || t.Exception != null)
                     {

                         if (t.Exception is AggregateException)
                         {
                             foreach (var innerEx in t.Exception.InnerExceptions)
                             {
                                 Console.WriteLine(innerEx);
                             }
                         }
                         else
                         {
                             Console.WriteLine(t.Exception);
                         }
                     }
                 })
                 );
            }

            Task.WaitAll(taskList.ToArray());
            sw.Stop();
            Console.WriteLine(string.Format("it takes {0} ms", sw.ElapsedMilliseconds));
        }
    }
}
